﻿/**
 * Copyright (C) 2011 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Reflection;
using NGuice.Inject.Internal.Util;

namespace NGuice.Inject.Internal
{
    /// <summary>
    /// 元特性的工具类
    /// </summary>
    public static class Attributes
    {
        /// <summary>
        /// 判断指定元特性是否为一个标记，不具有其他任何属性
        /// </summary>
        public static bool IsMarker(Type attributeType)
        {
            return attributeType.GetMembers().Length == 0;
        }

        /// <summary>
        /// 返回在类型上的ScopeAnnotationAttribute元注释，如果没有则返回null
        /// </summary>
        /// <param name="errors"></param>
        /// <param name="implementation"></param>
        /// <returns></returns>
        public static Type FindScopeAttribute(Errors errors, Type implementation)
        {
            return FindScopeAttribute(errors, implementation.GetCustomAttributes(true));
        }

        /** Returns the scoping annotation, or null if there isn't one. */
        public static Type FindScopeAttribute(Errors errors, object[] attributes)
        {
            Type found = null;

            foreach (Attribute annotation in attributes)
            {
                Type annotationType = annotation.GetType();
                if (IsScopeAnnotation(annotationType))
                {
                    if (found != null)
                    {
                        errors.DuplicateScopeAnnotations(found, annotationType);
                    }
                    else
                    {
                        found = annotationType;
                    }
                }
            }

            return found;
        }

        /// <summary>
        /// 对元特性进行检查的工具类，此检查类是检查传入的元特性是否具有本检查器要检测的元特性修饰
        /// 他能够自动实现缓存算法将结果进行缓存，这个缓存实现会定期清除，如果缓存中不能存在，
        /// 将自动使用Func委托进行判断
        /// </summary>
        /// //TODO:目前时间仓促，缓存只是使用普通的并发哈希表存储，不会定期清空。在将来的版本中会改进！
        class AttributeChecker
        {
            private ICollection<Type> attributeTypes;

            /** Returns true if the given class has one of the desired annotations. */
            private Func<Type, bool> hasAttributes;

            //TODO:目前时间仓促，缓存只是使用普通的并发哈希表存储。在将来的版本中将直接从缓存中获取，如果缓存中不存在则自动执行Func操作判断
            IDictionary<Type, bool> cache;

            /// <summary>
            /// 使用给定的元特性类型构造检查器
            /// </summary>
            internal AttributeChecker(ICollection<Type> attributeTypes)
            {
                //Debug.Assert(typeof(Attribute).IsAssignableFrom(attributeTypes.GetType().GetGenericArguments()[0]));
                foreach (Type t in attributeTypes)
                {
                    if (!typeof(Attribute).IsAssignableFrom(t))
                        throw new ArgumentException("the collection attributeTypes elements's type must be Attribute Type");
                }
                this.attributeTypes = attributeTypes;
                hasAttributes = t =>
                {
                    foreach (Attribute attribute in t.GetCustomAttributes(true))
                    {
                        if (this.attributeTypes.Contains(attribute.GetType()))
                        {
                            return true;
                        }
                    }
                    return false;
                };
                cache = new MapMaker().WeakKeys().MakeComputingMap(hasAttributes);
            }

            /// <summary>
            /// 判断指定元特性类型是否具有被此检查器中的元特性修饰
            /// </summary>
            internal bool HasAttributes(Type attributeTypes)
            {
                //Debug.Assert(typeof(Attribute).IsAssignableFrom(attributeTypes.GetType().GetGenericArguments()[0]));
                if (!typeof(Attribute).IsAssignableFrom(attributeTypes))
                    throw new ArgumentException("the attributeTypes must be Attribute Type"); 
                if (cache.ContainsKey(attributeTypes))
                    return cache[attributeTypes];
                else
                {
                    cache.Add(attributeTypes, hasAttributes(attributeTypes));
                    return cache[attributeTypes];
                }
            }
        }

        private static readonly AttributeChecker scopeChecker = new AttributeChecker(new Type[] { typeof(ScopeAnnotationAttribute) }.ToList());

        public static bool IsScopeAnnotation(Type annotationType)
        {
            return scopeChecker.HasAttributes(annotationType);
        }

        /// <summary>
        /// 如果在类型上注释错误，则添加errors，Scoping注释不允许放在接口或抽象类上
        /// </summary>
        /// <param name="type"></param>
        /// <param name="source"></param>
        /// <param name="errors"></param>
        public static void CheckForMisplacedScopeAnnotations(Type type, object source, Errors errors)
        {
            if (Classes.IsConcrete(type))
            {
                return;
            }

            Type scopeAnnotation = Attributes.FindScopeAttribute(errors, type.GetCustomAttributes(true));
            if (scopeAnnotation != null)
            {
                errors.WithSource(type).ScopeAnnotationOnAbstractType(scopeAnnotation, type, source);
            }
        }


        /// <summary>
        /// 获取指定类型、域和元特性的key（从给定元特性列表中获取Binding元特性）
        /// </summary>
        /// <param name="type">member成员的声明类型</param>
        /// <exception cref="ErrorsException"></exception>
        /// <returns>泛型参数为type类型的Key（泛型类型为type类型）</returns>
        public static Key GetKey(Type type, MemberInfo member, Attribute[] annotations, Errors errors)
        {
            int numErrorsBefore = errors.Size;
            Attribute found = FindBindingAnnotation(errors, member, annotations);
            errors.ThrowIfNewErrors(numErrorsBefore);
            //使用type通过反射构造泛型化参数类型
            Type genericType = typeof(Key<>).GetGenericTypeDefinition().MakeGenericType(new Type[] { type });
            return found == null
                ? (Key)genericType.GetMethod("Get", BindingFlags.Static | BindingFlags.Public, null, null, null).Invoke(null, null)
                : (Key)genericType.GetMethod("Get", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(Attribute) }, null).Invoke(null, new object[] { found });
        }

        /// <summary>
        /// 获取在指定member上的元特性，如果没有则为null
        /// </summary>
        public static Attribute FindBindingAnnotation(Errors errors, MemberInfo member, Attribute[] annotations)
        {
            Attribute found = null;
            foreach (Attribute annotation in annotations)
            {
                Type annotationType = annotation.GetType();
                if (IsBindingAttribute(annotationType))
                {
                    if (found != null)
                    {
                        errors.DuplicateBindingAnnotations(member, found.GetType(), annotationType);
                    }
                    else
                    {
                        found = annotation;
                    }
                }
            }
            return found;
        }

        private static readonly AttributeChecker bindingAttributeChecker = new AttributeChecker(new Type[] { typeof(BindingAnnotationAttribute) }.ToList());


        /// <summary>
        /// 检测指定元特性类型是否是被BindingAnnotationAttribute修饰的元特性
        /// </summary>
        public static bool IsBindingAttribute(Type attributeType)
        {
            return bindingAttributeChecker.HasAttributes(attributeType);
        }

        public static bool AllowsNull(Attribute[] annotations)
        {
            foreach (Attribute a in annotations)
            {
                Type type = a.GetType();
                if ("Nullable".Equals(type.Name) || type == typeof(Nullable))
                {
                    return true;
                }
            }
            return false;
        }


    }
}
